﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BamiTech.AirServer
{
    public class HTTPLikeRequest
    {
        public List<byte> Body { get; private set; } = new List<byte>();
        public Dictionary<string, string> HeaderPairs { get; private set; } = new Dictionary<string, string>();

        public string Method { get; private set; } = "";
        public string Url { get; private set; } = "";
        public string Protocol { get; private set; } = "";

        public string RequestLine()
        {
            return $"{Method} {Url} {Protocol}";
        }

        public enum ParseStatus
        {
            eWaitingRequestLineEnd,
            eWaitingHeaderEnd,
            eWaitingContentEnd,
            eFinish
        }

        private ParseStatus parseStatus = ParseStatus.eWaitingRequestLineEnd;


        private List<byte> UnparsedHeaderLine = new List<byte>();
        private Int64? ContentLength = null;
        private static byte[] NextLineBytes = new byte[] { 0xD, 0xA};

        public bool Finished()
        {
            return parseStatus == ParseStatus.eFinish;
        }

        public string GetHeader(string key)
        {
            string v = "";
            HeaderPairs.TryGetValue(key, out v);
            return v;
        }

        private void ParseRequestLineOrHeaderPair(byte[] headerLine)
        {
            string lineContent = UTF8Encoding.UTF8.GetString(headerLine);
            if (parseStatus == ParseStatus.eWaitingRequestLineEnd)
            {
                parseStatus = ParseStatus.eWaitingHeaderEnd;
                string[] requestLineComponents = lineContent.Split(" ");
                Debug.Assert(requestLineComponents.Count() == 3);
                Method      = requestLineComponents[0];
                Url         = requestLineComponents[1];
                Protocol    = requestLineComponents[2];
            }
            else
            {
                int index = lineContent.IndexOf(':');
                if (index == -1)
                {
                    HeaderPairs.Add(lineContent, "");
                }
                else
                {
                    string key = lineContent.Substring(0, index);
                    string value = lineContent.Substring(index + 1, lineContent.Length - index - 1).Trim();
                    HeaderPairs[key] = value;
                    if (key == "Content-Length")
                    {
                        long contentLength = 0;
                        if (Int64.TryParse(value, out contentLength))
                        {
                            ContentLength = contentLength;
                        }
                    }
                }
            }
        }
        
        public int Parse(Byte[] bytes)
        {
            if ((parseStatus == ParseStatus.eWaitingRequestLineEnd) || (parseStatus == ParseStatus.eWaitingHeaderEnd))
            {
                if ((UnparsedHeaderLine.Count != 0) && (UnparsedHeaderLine.Last() == '\r') && (bytes[0] == '\n'))
                {
                    ParseRequestLineOrHeaderPair(UnparsedHeaderLine.SkipLast(1).ToArray());
                    UnparsedHeaderLine.Clear();
                    return Parse(bytes.Skip(1).ToArray()) + 1;
                }
                else
                {
                    if ((bytes.Count() >= 2) && bytes.Take(2).SequenceEqual(NextLineBytes))
                    {
                        if (UnparsedHeaderLine.Count != 0)
                        {
                            ParseRequestLineOrHeaderPair(UnparsedHeaderLine.ToArray());
                            UnparsedHeaderLine.Clear();
                            return Parse(bytes.Skip(2).ToArray()) + 2;
                        }
                        else
                        {
                            Debug.Assert(parseStatus == ParseStatus.eWaitingHeaderEnd);
                            parseStatus = ParseStatus.eWaitingContentEnd;
                            return Parse(bytes.Skip(2).ToArray()) + 2;
                        }
                    }
                    else
                    {
                        int firstFindedIndex = ByteArrayRocks.IndexOf(bytes, 0, NextLineBytes).FirstOrDefault();                        
                        if (firstFindedIndex == 0)
                        {
                            // not find
                            UnparsedHeaderLine.AddRange(bytes);
                            return bytes.Count();
                        }
                        else
                        {
                            UnparsedHeaderLine.AddRange(bytes.Take(firstFindedIndex));
                            ParseRequestLineOrHeaderPair(UnparsedHeaderLine.ToArray());
                            UnparsedHeaderLine.Clear();
                            return Parse(bytes.Skip(firstFindedIndex + 2).ToArray()) + firstFindedIndex + 2;
                        }
                    }
                }
            }
            else if (parseStatus == ParseStatus.eWaitingContentEnd)
            {                
                Int64 ContentLengthValue = ContentLength.GetValueOrDefault();
                int takedCount = 0;
                if (Body.Count < ContentLengthValue)
                {
                    var takedBytes = bytes.Take((int)ContentLengthValue - Body.Count);
                    takedCount = takedBytes.Count();
                    Body.AddRange(takedBytes);
                }
                if (Body.Count == ContentLengthValue)
                {
                    parseStatus = ParseStatus.eFinish;
                }
                return takedCount;
            }
            throw new InvalidOperationException();
        }
    }
}
